<template>
    <div class="upload-storage">
        <el-upload ref="uploadStorageRef" list-type="picture-card" 
            :limit="upload.limit"
            :accept="upload.accept"
            :auto-upload="false"
            :disabled="readonly"
            :action="upload.action"
            :headers="upload.headers"
			:data="upload.data"
            :name="upload.name"
            :file-list="upload.images"
            :before-upload="handleBeforeUpload"
            :on-change="handleUploadChange"
            :on-exceed="handleUploadExceed"
            :on-preview="handleUploadPreview"
            :on-success="handleUploadSuccess"
            :on-remove="handleUploadRemove">
			<template #trigger>
				<el-icon><plus /></el-icon>
			</template>
			<template #file="{ file }">
				<div style="margin: 0 auto;">
					<img class="el-upload-list__item-thumbnail" v-if="isPictureFileExtension(file)" :src="getStorageAccessTokenUrl(file.url)" alt="" />
					<img class="el-upload-list__item-thumbnail" v-else :src="`${publicUrl}images/filetype/${getUploadFileExtension(file)}.png`" alt="" />
					<span class="el-upload-list__item-actions">
						<span class="el-upload-list__item-preview" @click="handleUploadPreview(file)">
							<el-icon><zoom-in /></el-icon>
						</span>
						<span class="el-upload-list__item-delete" @click="handleBeforeDownload(file)">
							<el-icon><Download /></el-icon>
						</span>
						<span class="el-upload-list__item-delete" v-if="!readonly" @click="handleUploadRemove(file)">
							<el-icon><Delete /></el-icon>
						</span>
					</span>
				</div>
			</template>
            <template #tip>
                <div class="el-upload__tip color-warning">
                    <p>请上传文件，仅限{{upload.limit}}个文件，50MB以下，允许格式{{upload.accept}}</p>
                </div>
            </template>
        </el-upload>
        <el-dialog v-model="upload.dialogVisible" width="80%">
            <img class="w100" :src="getStorageAccessTokenUrl(upload.storage)" alt=""/>
        </el-dialog>
        <CropperDialog ref="cropperDialogRef" :model="cropper.options" @submit="onCropperDialogSubmit" @cancel="onCropperDialogCancel"/>
    </div>
</template>
<script lang="ts">
import { ref, toRefs, reactive, onMounted, onUnmounted, watch } from 'vue';
import { Session } from '/@/utils/storage';
import { ElMessage } from 'element-plus';
import { Plus, Picture, Delete, Download, ZoomIn } from '@element-plus/icons-vue';
import CropperDialog from '/@/views/crm/components/cropper/index.vue';
import { Sign } from '/@/utils/sign';
import { uploadStorage } from '/@/api/crm/storage/index';
export default {
	name: 'crm/components/upload',
	emits: ['success', 'remove'],
	components: { Plus, Picture, Delete, Download, ZoomIn, CropperDialog },
	props: {
		// 绑定值
		show: {
			type: Boolean,
			default: () => true,
		},
		readonly: {
			type: Boolean,
			default: () => false,
		},
		model: {
			type: Object,
			default: () => {},
		}
	},
	setup(props, context) {
		const publicUrl = import.meta.env.VITE_PUBLIC_PATH as string;
		const serverUrl = import.meta.env.VITE_SERVER_URL as string;
		const uploadStorageRef = ref();
		const cropperDialogRef = ref();
		const state = reactive({
			isShow: true,
			readonly: false,
			publicUrl: publicUrl,
			upload: {
				action: '/upload',
				limit: 1,
				accept: '.jpg,.png,.svg,.webp,.gif',
				headers: {},
				name: 'file',
				images: [],
				dialogVisible : false,
				storages: [],
                storage: '',
				data: {},
			},
			cropper: {
				options: {
					aspectRatio: 0,
				}
			},
			hasCropper: false,
			hasModified: false,
			extensions: {
				docs: ['.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.pdf', '.txt'],
				pictures: ['.bmp', '.gif', '.jpg', '.jpeg', '.png', '.ico', '.webp', '.svg'],
			}
		});
        //上传之前
		const handleBeforeUpload = async (file) => {
			if(file.size >= 50 * 1024 * 1024){//50M
				ElMessage.error('文件大小最大限制 50MB 以下');
				return false;
			}
			// 参数签名
			const options = {
				url: uploadStorage(),
				params: {
					file: file.name,
				}
			}
			const { signature, secret, timestamp } = Sign.signature(options);
			state.upload = Object.assign(state.upload, {
				action: serverUrl + uploadStorage(),
				headers: {
					Authorization: 'Bearer ' + Session.get('token'),
					signature: signature,
					secret: secret,
					timestamp: timestamp
				},
				data: options.params
			});
			
			return true;
		}
		//下载之前
		const handleBeforeDownload = (file) => {
			// 创建隐藏的可下载链接
			const element = document.createElement('a');
			element.setAttribute('href', getStorageAccessTokenUrl(file.url));
			element.setAttribute('download', ''); // 可以指定文件名，例如：'filename.pdf'
			
			// 触发点击
			document.body.appendChild(element);
			element.click();
			
			// 然后移除
			document.body.removeChild(element);
		}
		//文件状态改变时
		const handleUploadChange = (file) => {
			if(file.status == 'ready') {
				const ext = file.name.substring(file.name.lastIndexOf('.'));
				if(file.size > 0 && state.upload.accept.indexOf(ext) != -1){
					// if(state.extensions.pictures.indexOf(ext) != -1 && state.hasCropper == false){
					// 	cropperDialogRef.value.openDialog(file);
					// }else{
					// 	uploadStorageRef.value.submit();
					// }
					uploadStorageRef.value.submit();
				}else{
					ElMessage.warning('请选择' + state.upload.accept + '格式的文件');
				}
			}
		}
		const handleUploadExceed = () => {
			ElMessage.warning('最多允许上传' + state.upload.limit + '个文件');
		}
		//预览
		const handleUploadPreview = (file) => {
			if(file.url){
				const ext = file.url.substring(file.url.lastIndexOf('.'));
				if(state.extensions.pictures.indexOf(ext) != -1){
					state.upload.dialogVisible = true;
					state.upload.storage = file.url;
				}else{
					window.open(getStorageAccessTokenUrl(file.url));
				}
			}
		};
		//上传成功
		const handleUploadSuccess = (res, file) => {
			if(file.status == 'success'){
				state.hasCropper = false;
				state.hasModified = false;
				state.upload.dialogVisible = false;
                state.upload.storage = '';
				file.url = file.response.data.url;
				file.name = file.response.data.origin;
				state.upload.storages.push(file.url);
				state.upload.images.push({'url': file.url});
				context.emit('success', file, state.upload.storages);
			}else {
				state.hasCropper = false;
				state.hasModified = false;
				state.upload.dialogVisible = false;
                state.upload.storage = '';
				ElMessage.warning(res.msg);
			}
		};
		//移除
		const handleUploadRemove = (file) => {
			state.upload.images = state.upload.images.filter(image => image.url !== file.url);
			state.upload.storages = state.upload.storages.filter(url => url !== file.url);
			context.emit('remove', file, state.upload.storages);
		};
		const onCropperDialogSubmit = async (file, hasModified, base64) => {
			if(hasModified) {//图片经过修改
				state.hasCropper = true;//图片已被剪辑
				await uploadStorageRef.value.handleRemove(file);
				await uploadStorageRef.value.handleStart(base64ToFile(base64, file.name));
			}else{
				await uploadStorageRef.value.submit();
			}
		}
		const onCropperDialogCancel = (file) => {
			uploadStorageRef.value.handleRemove(file);
		}
		//将base64转换为文件
		const base64ToFile = (base64, filename) => { 
			var arr = base64.split(','),
			mime = arr[0].match(/:(.*?);/)[1],
			bstr = atob(arr[1]),
			n = bstr.length,
			u8arr = new Uint8Array(n);
			while (n--) {
				u8arr[n] = bstr.charCodeAt(n);
			}
			return new File([u8arr], filename, {type: mime});
		}

		const isPictureFileExtension = (file) =>{
			if(file.status == 'success'){
				const suffix = file.url.substring(file.url.lastIndexOf('.'));
				return state.extensions.pictures.indexOf(suffix) != -1;
			}
			return true;
		}
		const getUploadFileExtension = (file) => {
			if(file.status == 'success'){
				return file.url.substring(file.url.lastIndexOf('.') + 1);
			}
			return 'file';
		}
		const getStorageAccessTokenUrl = (url) => {
			return url + '?accessToken=' + Session.get('token');
		}
		// 页面加载时
		onMounted(() => {
			uploadStorageRef.value?.clearFiles(['ready', 'uploading', 'success', 'fail']);
			state.upload = Object.assign(state.upload, props.model?.upload);
			state.cropper = Object.assign(state.cropper, props.model?.cropper);
			state.isShow = props.show;
			state.readonly = props.readonly;
		});
		onUnmounted(() => {
			uploadStorageRef.value?.clearFiles(['ready', 'uploading', 'success', 'fail']);
            state.upload = {
				action: '/upload',
				limit: 1,
				accept: '.jpg,.png,.svg,.webp,.gif',
				headers: {},
				name: 'file',
				images: [],
				dialogVisible : false,
				storages: [],
                storage: '',
				data: {},
			};
            state.cropper = {
				options: {
					aspectRatio: 0,
				}
			};
		})
		
		//监听model数据
		watch(() => props.model, () => {
			state.upload = Object.assign(state.upload, props.model?.upload);
			state.cropper = Object.assign(state.cropper, props.model?.cropper);
		}, {deep: true});
		watch(() => props.show, () => {
			state.isShow = props.show;
		}, {deep: true});
		watch(() => props.readonly, () => {
			state.readonly = props.readonly;
		}, {deep: true});

		return {
			uploadStorageRef,
			cropperDialogRef,
			handleBeforeUpload,
			handleBeforeDownload,
			handleUploadChange,
			handleUploadExceed,
			handleUploadPreview,
			handleUploadSuccess,
			handleUploadRemove,
			onCropperDialogSubmit,
			onCropperDialogCancel,
			isPictureFileExtension,
			getUploadFileExtension,
			getStorageAccessTokenUrl,
			...toRefs(state),
		};
	}
}
</script>